package job

import (
	"context"
	rlock "github.com/gotomicro/redis-lock"
	"go.uber.org/zap"
	"jk-time/webook/internal/service"
	"sync"
	"time"
)

type RankingJob struct {
	svc        service.RankingService
	timeout    time.Duration
	lockClient *rlock.Client
	key        string
	lock       *rlock.Lock
	localLock  *sync.Mutex
}

func NewRankingJob(svc service.RankingService, timeout time.Duration,
	lockClient *rlock.Client) *RankingJob {
	return &RankingJob{
		svc:        svc,
		timeout:    timeout, //ctx每一次执行时间
		lockClient: lockClient,
		key:        "cron_job:ranking",
		localLock:  &sync.Mutex{},
	}
}
func (r *RankingJob) Name() string {
	return "ranking"
}

// JOB FUNC RUN()
// 持有锁后，就一直不放，除非宕机
func (r *RankingJob) Run() error {
	r.localLock.Lock()
	defer r.localLock.Unlock()
	if r.lock == nil {
		//无🔒，尝试拿🔒
		ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
		defer cancel()
		//设置比较短的过期时间
		lock, err := r.lockClient.Lock(ctx, r.key, r.timeout,
			// 每隔 100ms 重试一次，每次重试的超时时间是 1s
			&rlock.FixIntervalRetry{
				Interval: time.Millisecond * 100,
				Max:      0,
			}, time.Second)
		if err != nil {
			//拿不到🔒,大概率别人拿到了🔒
			return nil
		}
		r.lock = lock
		//怎么保证，一直拿着🔒？
		go func() {
			//自动续约
			err = lock.AutoRefresh(r.timeout-10, time.Second)
			//续约失败,自己和redis失去连接
			if err != nil {
				r.localLock.Lock()
				zap.L().Info("redis续约失败")
				r.lock = nil
				defer r.localLock.Unlock()
			}
		}()
	}

	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
	defer cancel()
	return r.svc.TopN(ctx)
}
func (r *RankingJob) Close() error {
	r.localLock.Lock()
	lock := r.lock
	r.localLock.Unlock()

	//释放锁
	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
	defer cancel()
	//释放锁失败，也不需要作什么，过期时间也会自动释放
	return lock.Unlock(ctx)
}
